using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class TurretGame : Microsoft.Xna.Framework.Game
    {
        enum GameStates { Splash, Playing, Score};

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        #region Member Variables
        /// <summary>
        /// The camera for the game
        /// </summary>
        Camera mCamera;

        /// <summary>
        /// Our turret model
        /// </summary>
        Turret mTurret;

        RatSpawn ratSpawn;
        RatSpawn superRatSpawn;

        /// <summary>
        /// The current state of the keyboard.
        /// </summary>
        private KeyboardState mLastKeyboardState;

        private GrassField mGrassField;

        private Vector2 mViewPortCenter;

        //private float mSplashScreenTime = 2.0f;

        private float mFlashTime = 0.5f;

        private GameStates mGameState;

        Texture2D splashTexture;

        private SpriteFont levelFont;

        private SpriteFont scoreFont;

        private SpriteFont continueFont;

        private int totalScore;


        /// <summary>
        /// A reference to the audio engine we use
        /// </summary>
        AudioEngine audioEngine;

        /// <summary>
        /// The loaded audio wave bank
        /// </summary>
        WaveBank waveBank;

        /// <summary>
        /// The loaded audio sound bank
        /// </summary>
        SoundBank soundBank;

        private Bat bat;


        private SpriteFont mScoreFont;


        #endregion

        #region Properties

        public Camera Camera { get { return mCamera; } }
        public SoundBank SoundBank { get { return soundBank; } }
        #endregion

        public TurretGame()
        {
            graphics = new GraphicsDeviceManager(this);

            mGameState = GameStates.Splash;
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;
            graphics.ApplyChanges();
            mViewPortCenter = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2,
                                          graphics.GraphicsDevice.Viewport.Height / 2);
            
            Content.RootDirectory = "Content";

            mTurret = new Turret(this);
            bat = new Bat(this);
            ratSpawn = new RatSpawn(this, 15, false);
            superRatSpawn = new RatSpawn(this, 2, true);
            mGrassField = new GrassField(this);
            mCamera = new Camera(graphics);
            mCamera.UseChaseCamera = false;
            this.IsMouseVisible = true;


            
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            mCamera.Initialize();

            //DO NOT DELETE CAMERA LOCATION FOR THE GAME
            //>>>>mCamera.Eye = new Vector3(-50,200, 0);<<<<<<
            mCamera.Eye = new Vector3(-100, 200, 10);
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            //mScoreFont = Content.Load<SpriteFont>("scorefont");

            mTurret.LoadContent(Content);
            ratSpawn.LoadContent(Content);
            superRatSpawn.LoadContent(Content);
            mGrassField.LoadContent(Content);
            bat.LoadContent(Content);
            splashTexture = Content.Load<Texture2D>("SplashScreen");
            levelFont = Content.Load<SpriteFont>("Level");
            scoreFont = Content.Load<SpriteFont>("Score");
            continueFont = Content.Load<SpriteFont>("Continue");
            audioEngine = new AudioEngine("Content\\XwingAudio.xgs");
            waveBank = new WaveBank(audioEngine, "Content\\Wave Bank.xwb");
            soundBank = new SoundBank(audioEngine, "Content\\Sound Bank.xsb");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            
            switch (mGameState)
            {
                case GameStates.Splash:
                    {
                        KeyboardState lKeyboardState = Keyboard.GetState();
                        if (lKeyboardState.IsKeyDown(Keys.Enter) && mLastKeyboardState.IsKeyUp(Keys.Enter))
                        {
                            mGameState = GameStates.Playing;
                            ResetXNAStates();
                        }
                        //System.Diagnostics.Trace.WriteLine(mSplashScreenTime.ToString());
                        
                        break;
                    }
                case GameStates.Playing:
                    {

                        // ADD ANY KEYBOARD LOGIC HERE
                        KeyboardState lKeyboardState = Keyboard.GetState();

                        if (lKeyboardState.IsKeyDown(Keys.Space) && mLastKeyboardState.IsKeyUp(Keys.Space))
                        {
                            mTurret.FireCannon();
                        }
                        if (lKeyboardState.IsKeyDown(Keys.Left))
                        {
                            mTurret.RotationRate = 1;
                        }
                        else if (lKeyboardState.IsKeyDown(Keys.Right))
                        {
                            mTurret.RotationRate = -1;
                        }
                        else
                        {
                            mTurret.RotationRate = 0;
                        }

                        if (lKeyboardState.IsKeyDown(Keys.Up))
                        {
                            mTurret.ElevationRate = 1;
                        }
                        else if (lKeyboardState.IsKeyDown(Keys.Down))
                        {
                            mTurret.ElevationRate = -1;
                        }
                        else
                        {
                            mTurret.ElevationRate = 0;
                        }

                        mLastKeyboardState = lKeyboardState;


                        // PUT YOUR UPDATE CALLS HERE
                        mTurret.Update(gameTime);
                        mGrassField.Update(gameTime);
                        bat.Update(gameTime);
                        if(ratSpawn.ratsLeft == 0)
                            superRatSpawn.Update(gameTime);
                        else
                            ratSpawn.Update(gameTime);
                        // Camera updates
                        Vector3 lTemp = new Vector3(-50, 200, 0);
                        Vector3 lTemp2 = new Vector3(-1000, 200, 0);
                        Matrix lRotation = Matrix.CreateRotationY(mTurret.Rotation);
                        mCamera.Eye = Vector3.Transform(lTemp, lRotation);
                        mCamera.Center = Vector3.Transform(lTemp2, lRotation);
                        mCamera.Update(gameTime);

                        for (LinkedListNode<LaserFire.LaserBlast> lBlastNode = mTurret.pLaserFire.LaserBlasts.First; lBlastNode != null; )
                        {
                            LaserFire.LaserBlast lBlast = lBlastNode.Value;
                            LinkedListNode<LaserFire.LaserBlast> lNextBlast = lBlastNode.Next;
                            
                            //Insert Logic for testLaserForCollision for both types of assests.
                            ratSpawn.TestLaserForCollision(lBlast.mPosition);
                            if(ratSpawn.ratsLeft == 0)
                                superRatSpawn.TestLaserForCollision(lBlast.mPosition);

                            lBlastNode = lNextBlast;
                        }
                        break;
                    }

                case GameStates.Score:
                    {
                        KeyboardState lKeyboardState = Keyboard.GetState();
                        mCamera.Eye = new Vector3(1000, 1000, 1000);
                        mCamera.Center = new Vector3(0,0,0);

                        if (lKeyboardState.IsKeyDown(Keys.Enter) && mLastKeyboardState.IsKeyUp(Keys.Enter))
                        {
                            mGameState = GameStates.Playing;
                            mTurret.Reset();
                            ratSpawn.Reset();
                            superRatSpawn.Reset();
                            ResetXNAStates();
                        }
                        break;
                    }

                default:
                    break;
            }
            // TODO: Add your update logic here

            audioEngine.Update();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            
            switch (mGameState)
            {
                case GameStates.Splash:
                    {

                        spriteBatch.Begin();
                        spriteBatch.Draw(splashTexture, new Vector2(0, 0), Color.White);
                        if (mFlashTime > 0)
                        {
                            spriteBatch.DrawString(continueFont, "Press Enter to Continue...", new Vector2(330, 550), Color.White);
                        }
                        else if (mFlashTime < -.5f)
                        {
                            mFlashTime = .5f;
                        }
                        spriteBatch.End(); 
                        mFlashTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                        break;
                    }
                case GameStates.Playing:
                    {
                        // PUT NOTHING BEFORE THE RESETXNASTATES FUNCTION CALL
                        // It's needed for converting back and forth between 3d and 2d
                        ResetXNAStates();
                        // 3d
                        mTurret.Draw(graphics, gameTime);

                        Matrix[] lTransforms = new Matrix[mTurret.TurretModel.Bones.Count];
                        mTurret.TurretModel.CopyAbsoluteBoneTransformsTo(lTransforms);

                        foreach (ModelMesh lMesh in mTurret.TurretModel.Meshes)
                        {
                            BoundingSphere lBS = lMesh.BoundingSphere;
                            if (ratSpawn.TestForDeath(lBS))
                                mGameState = GameStates.Score;

                            if (superRatSpawn.TestForDeath(lBS))
                                mGameState = GameStates.Score;
                        }
                        mGrassField.Draw(graphics, gameTime);
                        bat.Draw(graphics, gameTime);
       
                        if (ratSpawn.ratsLeft == 0)
                        {
                            superRatSpawn.Draw(graphics, gameTime);
                            spriteBatch.Begin();
                            spriteBatch.DrawString(levelFont, "Level Two", new Vector2(0, 0), Color.White);
                            totalScore = ratSpawn.Score + superRatSpawn.Score;
                            string scoreString = totalScore.ToString();
                            spriteBatch.DrawString(levelFont, "Score: " + scoreString, new Vector2(450, 0), Color.White);
                            spriteBatch.End();
                            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                        }
                        else
                        {
                            ratSpawn.Draw(graphics, gameTime);
                            spriteBatch.Begin();
                            spriteBatch.DrawString(levelFont, "Level One" , new Vector2(0, 0), Color.White);
                            totalScore = ratSpawn.Score + superRatSpawn.Score;
                            string scoreString = totalScore.ToString();
                            spriteBatch.DrawString(levelFont, "Score: " + scoreString, new Vector2(450, 0), Color.White);
                            spriteBatch.End();
                            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                        }

                        // 2d
                        RadarDraw lRadar = new RadarDraw(graphics.GraphicsDevice);
                        spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
                        lRadar.CreateRadar(spriteBatch);
                        
                        /*for (LinkedListNode<RatSpawn.CRat> lRatNode = ratSpawn.RatsList.First; lRatNode != null; )
                        {
                            LinkedListNode<RatSpawn.CRat> lNext = lRatNode.Next;
                            RatSpawn.CRat lRat = lRatNode.Value;

                            Vector3 lRatPosition = lRat.position;
                            lRatPosition /= 3000;

                            Vector2 lXZPosition = new Vector2(-lRatPosition.X, -lRatPosition.Z);

                            lRadar.DrawX(spriteBatch, mScoreFont,lXZPosition);

                        }*/
                        spriteBatch.End();
                        break;
                    }

                case GameStates.Score:
                    {
                        ResetXNAStates();
                        mTurret.Draw(graphics, gameTime);
                        mGrassField.Draw(graphics, gameTime);
                        superRatSpawn.Draw(graphics, gameTime);
                        ratSpawn.Draw(graphics, gameTime);
                        spriteBatch.Begin();
                        totalScore = ratSpawn.Score + superRatSpawn.Score;
                        string scoreString = totalScore.ToString();
                        spriteBatch.DrawString(levelFont, "Game Over", new Vector2(445,0), Color.White);
                        spriteBatch.DrawString(levelFont, "Score: " + scoreString, new Vector2(430, 25), Color.White);
                        if (mFlashTime > 0)
                        {
                            spriteBatch.DrawString(continueFont, "Press Enter to Play Again", new Vector2(330, 550), Color.White);
                        }
                        else if (mFlashTime < -.5f)
                        {
                            mFlashTime = .5f;
                        }
                        spriteBatch.End();
                        mFlashTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                        GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                        break;
                    }

                default:
                        break;
            }


            base.Draw(gameTime);
        }

        /// <summary>
        /// This function must be called when switching from 2d to 3d
        /// it restores the proper states for xna
        /// </summary>
        public void ResetXNAStates()
        {
            graphics.GraphicsDevice.BlendState = BlendState.AlphaBlend;
            graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphics.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
        }
    }
}
